package com.atguigu.tingshu.payment.service.impl;

import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.payment.PaymentInfo;
import com.atguigu.tingshu.payment.config.WxPayV3Config;
import com.atguigu.tingshu.payment.service.PaymentInfoService;
import com.atguigu.tingshu.payment.service.WxPayService;
import com.atguigu.tingshu.payment.util.PayUtil;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import io.seata.spring.annotation.GlobalTransactional;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class WxPayServiceImpl implements WxPayService {

    @Autowired
    private PaymentInfoService paymentInfoService;

    @Autowired
    private RSAAutoCertificateConfig rsaAutoCertificateConfig;

    @Autowired
    private WxPayV3Config wxPayV3Config;

    /**
     * 获取小程序拉起微信支付所需参数
     *
     * @param paymentType 支付方式： 1301：微信 1302：支付宝   1303：余额
     * @param orderNo     订单编号
     * @return Map封装小程序所需参数
     */
    @Override
    public Map<String, String> getClientPayRequestParams(String paymentType, String orderNo) {
        try {
            Long userId = AuthContextHolder.getUserId();
            //1.保存本地交易记录
            PaymentInfo paymentInfo = paymentInfoService.savePaymentInfo(paymentType, orderNo, userId);
            if (paymentInfo != null && SystemConstant.PAYMENT_STATUS_PAID.equals(paymentInfo.getPaymentStatus())) {
                throw new GuiguException(400, "该笔交易已支付，请勿重复提交！");
            }
            //2.服务端整合微信SDK-调用微信支付接口获取小程序拉起支付所需参数
            //2.1 创建调用获取小程序支付所需参数服务对象
            JsapiServiceExtension jsapiService = new JsapiServiceExtension.Builder().config(rsaAutoCertificateConfig).build();
            //2.2 创建预下单请求对象 封装请求体参数
            PrepayRequest request = new PrepayRequest();
            Amount amount = new Amount();
            //2.2.1 拉起微信支付需要用户所付钱金额 单位：分
            amount.setTotal(1);
            request.setAmount(amount);
            //2.2.2 每个同学都必须使用“官方”应用ID 使用自己会报错
            request.setAppid(wxPayV3Config.getAppid());
            //2.2.3 商户号
            request.setMchid(wxPayV3Config.getMerchantId());
            //2.2.4 购买商品描述
            request.setDescription(paymentInfo.getContent());
            //2.2.5 当用户支付成功后，微信会进行异步回调支付结果
            request.setNotifyUrl(wxPayV3Config.getNotifyUrl());
            //2.2.6 商户端全局唯一订单编号
            request.setOutTradeNo(paymentInfo.getOrderNo());
            //2.2.7 小程序并未正式上线，只允许小程序应用下关联开发者微信账户允许支付 大家使用我的openId 能够拉起微信支付 但不能付款
            Payer payer = new Payer();
            payer.setOpenid("odo3j4qp-wC3HVq9Z_D9C0cOr0Zs");
            request.setPayer(payer);
            //2.3 请求微信服务端获取小程序所需支付参数
            PrepayWithRequestPaymentResponse prepayResponse = jsapiService.prepayWithRequestPayment(request);
            if (prepayResponse != null) {
                Map<String, String> map = new HashMap<>();
                map.put("timeStamp", prepayResponse.getTimeStamp());
                map.put("package", prepayResponse.getPackageVal());
                map.put("paySign", prepayResponse.getPaySign());
                map.put("signType", prepayResponse.getSignType());
                map.put("nonceStr", prepayResponse.getNonceStr());
                return map;
            }
            return null;
        } catch (Exception e) {
            log.error("[支付服务]获取微信支付所需参数接口异常：{}", e);
            throw new RuntimeException(e);
        }
    }

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 用户通过微信支付支付成功后，处理微信支付异步回调
     * （微信通知商户用户支付结果）
     *
     * @param request
     * @return {code:"SUCCESS",message:"处理成功"}
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class) //事务发起方开启全局事务
    public Map<String, String> wxPaySuccessNotify(HttpServletRequest request) {
        //1.签名验证-避免出现“假通知”或者数据在网格传输中被恶意篡改
        //1.1 通过请求头获取构建RequestParam相关参数值
        String wechatPaySerial = request.getHeader("Wechatpay-Serial");  //签名
        String nonce = request.getHeader("Wechatpay-Nonce");  //签名中的随机数
        String timestamp = request.getHeader("Wechatpay-Timestamp"); //时间戳
        String signature = request.getHeader("Wechatpay-Signature"); //签名类型
        //通过工具类将所有请求体参数获取到
        String requestBody = PayUtil.readData(request);
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(wechatPaySerial)
                .nonce(nonce)
                .signature(signature)
                .timestamp(timestamp)
                .body(requestBody)
                .build();
        //1.2 初始化NotificationParser通知验签对象
        NotificationParser parser = new NotificationParser(rsaAutoCertificateConfig);

        //1.3 基于NotificationParser完成验签、解密并转换成 Transaction（交易对象）
        Transaction transaction = parser.parse(requestParam, Transaction.class);
        log.info("微信验签解密得到交易对象：{}", transaction);
        //2.业务验证
        //2.1 利于Redis set ex nx 幂等性处理 正常业务只处理一次
        String orderNo = transaction.getOutTradeNo();//商户侧订单编号
        String key = "order:notify:" + orderNo;
        Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, "", 25, TimeUnit.HOURS);
        if (flag) {
            try {
                //2.2 验证订单编号对应金额跟支付成功金额是否一致 TODO:实际产生支付金额1分，本地交易记录中存放实际应付金额
                Integer payerTotal = transaction.getAmount().getPayerTotal();
                if (payerTotal.intValue() == 1) {
                    //3.处理本地交易记录-变更本地交易记录状态
                    paymentInfoService.updatePaymentStatus(transaction, SystemConstant.ORDER_PAY_WAY_WEIXIN);
                    //4.构建结果响应微信支付端
                    Map<String, String> map = new HashMap<>();
                    map.put("code", "SUCCESS");
                    map.put("message", "操作成功！");
                    return map;
                }
            } catch (Exception e) {
                log.error("[支付服务]处理异步回调异常", e);
                redisTemplate.delete(key);
                throw new RuntimeException(e);
            }
        }
        return null;
    }

    /**
     * 商户主动调用微信支付获取订单支付状态
     *
     * @param orderNo
     * @return true：只支付  false：未支付
     */
    /*@Override
    public Boolean queryOrderPayStatus(String orderNo) {
        //1. 创建调用获取小程序支付所需参数服务对象
        JsapiServiceExtension jsapiService = new JsapiServiceExtension.Builder().config(rsaAutoCertificateConfig).build();
        //2.根据商户订单编号查询订单支付状态
        QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
        request.setMchid(wxPayV3Config.getMerchantId());
        request.setOutTradeNo(orderNo);
        Transaction transaction = jsapiService.queryOrderByOutTradeNo(request);
        if (transaction != null) {
            //3.1解析交易状态
            if (Transaction.TradeStateEnum.SUCCESS == transaction.getTradeState()) {
                //3.2 验证付款金额
                Integer payerTotal = transaction.getAmount().getPayerTotal();
                if (1 == payerTotal.intValue()) {
                    return true;
                }
            }
        }
        return false;
    }*/

    /**
     * 商户主动调用微信支付获取订单支付状态-同学版
     * 固定-支付是成功
     *
     * @param orderNo
     * @return true：只支付  false：未支付
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public Boolean queryOrderPayStatus(String orderNo) {
        //1.伪造一个微信交易对象
        Transaction transaction = new Transaction();
        transaction.setOutTradeNo(orderNo);
        transaction.setTransactionId("wx"+ IdUtil.getSnowflakeNextId());
        //2.更新本地交易记录状态
        paymentInfoService.updatePaymentStatus(transaction, SystemConstant.ORDER_PAY_WAY_WEIXIN);
        return true;
    }
}
